﻿/**
* @author Alexander Rindo
* @project Flashview
* @version 0.4
* @purpose Manages images
**/

package com.alexrindo.flashview.image 
{
	import caurina.transitions.*;
	import caurina.transitions.properties.DisplayShortcuts;
	import com.alexrindo.flashview.data.GalleryData;
	import com.alexrindo.flashview.layout.LayoutManager;
	import com.alexrindo.flashview.preloader.Preloader;
	import com.alexrindo.flashview.UI.ButtonManager;
	import com.alexrindo.flashview.UI.zoomSlider;
	import com.asual.swfaddress.*;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.net.FileReference;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.system.System;
	import flash.ui.Mouse;
	import flash.utils.Timer;
	import org.gif.events.FrameEvent;
	
	public class ImageManager extends Sprite
	{
		private static var Instance:ImageManager
		private static var Initializing:Boolean
		private var _imageContainer:Array = new Array()
		private var _currentImage:Image
		private var _imageURL:URLRequest
		private var _imageFile:FileReference
		private var _index:uint = 0
		private var _slider:zoomSlider
		private var _cursor:MoveCursor
		private var _data:GalleryData
		private var _layout:LayoutManager
		private var _buttons:ButtonManager
		private var _slideTimer:Timer
		
		public static var IMAGE_INIT:String = "image_init"
		public static var IMAGE_CHANGE:String = "image_change"
		public static var FRAME_CHANGE:String = "frame_change"
		public static var START_DRAG:String = "start_drag"
		public static var STOP_DRAG:String = "stop_drag"
		public static var IMAGE_MOVE:String = "image_move"
		public static var IMAGE_ZOOM:String = "image_zoom"
		
		private const FRAME_WIDTH:int = 7
		private const FRAME_OFFSET:int = 7
		
		private var _imageWidth:Number
		private var _imageHeight:Number
		
		private var _frame:Sprite
		private var _preloader:Preloader
		private var _saveloader:Preloader
		
		public function ImageManager() 
		{
			if (!Initializing)
			{
				throw new Error("This class is contructed as a singleton, please use the getInstance() method")
			}
			setup()
		}
		
		public static function getInstance():ImageManager
		{
			if (Instance == null)
			{
				Initializing = true
				Instance = new ImageManager()
				Initializing = false
			}
			return Instance
		}
		
		private function setup():void
		{
			DisplayShortcuts.init();
			
			_data = GalleryData.getInstance()
			_layout = LayoutManager.getInstance()
			
			_frame = new Sprite()
			addChild(_frame)
			
			addPreloader()
		}
		
		public function addListeners():void
		{
			useHandCursor = true
			buttonMode = true
			addEventListener(MouseEvent.MOUSE_DOWN, onStartDragImage)
			addEventListener(MouseEvent.MOUSE_UP, onStopDragImage)
			addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel)
			addEventListener(ImageManager.IMAGE_INIT, onImageInit)
			SWFAddress.addEventListener(SWFAddressEvent.CHANGE, addresChange)
			SWFAddress.addEventListener(SWFAddressEvent.INIT, checkFirstImage)
		}
		
		public function checkFirstImage(e:SWFAddressEvent):void
		{
			if (SWFAddress.getValue() != "/") { index = uint(e.value.split("_")[1]) } 
			addImage(index)
		}
		
		private function addresChange(e:SWFAddressEvent):void
		{
			if (e.value != "/")
			{
				SWFAddress.setTitle (_data.galleryName + " | " + _data.getCaption(index))
			} else {
				SWFAddress.setTitle (_data.galleryName)
			}
		}
		
		private function addPreloader()
		{
			_preloader = new Preloader()
			_preloader.x = -_preloader.width / 2
			_preloader.x = -_preloader.height / 2
			_layout.addStageObject(_preloader,{xDistance:0,yDistance:0},LayoutManager.CENTER)
			_preloader.visible = false
			addChild(_preloader)
		}
		
		public function addImage(index:uint):void
		{
			SWFAddress.setValue("photo_" + index)
			
			if (_currentImage != null) 
			{ 
				_currentImage.removeEventListener(ProgressEvent.PROGRESS, onImageProgress)
				_currentImage.stopLoad()
				removeChild(_currentImage)
			}
			
			_currentImage = _imageContainer[index]
			dispatchEvent(new Event(IMAGE_INIT))
			
			clearframe()
			
			if (_currentImage == null)
			{
				switch(_data.getType(index))
				{
					case "gif":
					_currentImage = new GIFImage(_data.imageFolderURL + _data.getImageURL(index))
					_currentImage.addEventListener(FrameEvent.FRAME_RENDERED, onFrameRendered)
					break
					
					default:
					_currentImage = new Image(_data.imageFolderURL + _data.getImageURL(index))
					break
				}
				_currentImage.addEventListener(ProgressEvent.PROGRESS, onImageProgress)
				_currentImage.addEventListener(Image.IMAGE_LOADED, setupImage)
				
			} else {
				if (!_currentImage.hasEventListener(ProgressEvent.PROGRESS))
				{
					
					_currentImage.addEventListener(ProgressEvent.PROGRESS, onImageProgress)
				}
				setupImage()
			}
			
			addChild(_currentImage)
		}
		
		private function onImageInit(e:Event):void
		{
			if (index < _data.pictureCount - 1) {createCache()}
			_slider.setSliderPosition(1)
		}
		
		private function onImageProgress(e:ProgressEvent):void
		{
			//trace("current image " + _currentImage.name)
			//trace("current target " + e.currentTarget.name)
			//trace("")
			if (e.currentTarget == _currentImage)
			{
			_preloader.visible = true
			_preloader.updatePreloader(Math.round((e.bytesLoaded / e.bytesTotal) * 100))
			}
		}
		
		private function setupImage(e:Event = null):void
		{
			if (isCurrentImageLoaded)
			{
				_preloader.visible = false
				_currentImage.resetScale()
				_currentImage.fadeIn()
				drawFrame()
			}
			dispatchEvent(new Event(IMAGE_CHANGE))
		}
		
		private function createCache():void
		{
			switch(_data.getType(index+1))
			{
				case "gif":
				_imageContainer[index + 1] = new GIFImage(_data.imageFolderURL + _data.getImageURL(index + 1))
				_imageContainer[index + 1].addEventListener(FrameEvent.FRAME_RENDERED, onFrameRendered)
				break
					
				default:
				_imageContainer[index + 1] = new Image(_data.imageFolderURL + _data.getImageURL(index + 1))
				_imageContainer[index + 1].addEventListener(ProgressEvent.PROGRESS, onImageProgress)
				break
			}
			_imageContainer[index + 1].addEventListener(Image.IMAGE_LOADED, setupImage)	
		}
		
		
		private function onStartDragImage(e:MouseEvent):void
		{
			_currentImage.startDrag(false)
			_cursor = new MoveCursor()
			_cursor.visible = false
			addChild(_cursor)
			Mouse.hide()
			
			addEventListener(MouseEvent.MOUSE_MOVE, onDraggingImage)
			dispatchEvent(new Event(ImageManager.START_DRAG))
		}
		
		private function onDraggingImage(e:MouseEvent):void
		{
			_cursor.x = e.stageX
			_cursor.y = e.stageY
			_cursor.visible = true
			dispatchEvent(new Event(ImageManager.IMAGE_MOVE))
			drawFrame()
		}
		
		private function onStopDragImage(e:MouseEvent):void
		{
			removeEventListener(MouseEvent.MOUSE_MOVE, onDraggingImage)
			dispatchEvent(new Event(ImageManager.STOP_DRAG))
			
			_currentImage.stopDrag()
			_cursor.visible = false
			Mouse.show()
		}
		
		private function onMouseWheel(e:MouseEvent):void
		{
			zoom(e.delta / 50, true)
			_slider.setSliderPosition(_currentImage.scaleX)
		}
		
		public function zoom(value:Number, relative:Boolean):void
		{
			if (relative == true)
			{
				_currentImage.scaleX += value
				_currentImage.scaleY += value
			} else {
				_currentImage.scaleX = value
				_currentImage.scaleY = value
			}
			dispatchEvent(new Event(ImageManager.IMAGE_ZOOM))
			if(isCurrentImageLoaded) { drawFrame() }
		}
		
		public function drawFrame():void
		{
			_frame.graphics.clear()
			_imageWidth = _currentImage.imageWidth
			_imageHeight = _currentImage.imageHeight
			_frame.x = getCurrentImagePosition().x
			_frame.y = getCurrentImagePosition().y
			_frame.graphics.beginFill(0xFFFFFF, 0.05)
			_frame.graphics.drawRect(-FRAME_OFFSET, -FRAME_OFFSET, _imageWidth + (FRAME_OFFSET*2) - FRAME_WIDTH, FRAME_WIDTH)
			_frame.graphics.drawRect(_imageWidth + FRAME_OFFSET - FRAME_WIDTH, -FRAME_OFFSET, FRAME_WIDTH, _imageHeight + (FRAME_OFFSET*2))
			_frame.graphics.drawRect( -FRAME_OFFSET, _imageHeight + FRAME_OFFSET - FRAME_WIDTH, _imageWidth + (FRAME_OFFSET * 2) - FRAME_WIDTH, FRAME_WIDTH)
			_frame.graphics.drawRect(-FRAME_OFFSET, -FRAME_OFFSET + FRAME_WIDTH, FRAME_WIDTH, _imageHeight + (FRAME_OFFSET*2)- (FRAME_WIDTH*2))
			_frame.graphics.endFill()
		}
		
		public function clearframe()
		{
			_frame.graphics.clear()
		}
		
		private function onFrameRendered(e:Event)
		{
			dispatchEvent(new Event(FRAME_CHANGE))
		}
		
		//Slideshow
		
		public function startSlideShow():void
		{
			if (_slideTimer == null) 
			{
				_slideTimer = new Timer(_data.slideShowTime, 0)
				_slideTimer.addEventListener(TimerEvent.TIMER, slideShow)
			}
			_slideTimer.start()
		}
		
		public function stopSlideShow():void
		{
			_slideTimer.stop()
		}
		
		public function slideShow(e:TimerEvent):void
		{
			nextImage()
		}
		
		public function checkSlideShow(start:Boolean):void
		{
			if(_buttons == null) { _buttons = ButtonManager.getInstance() }
			
			if (slideTimer.running == true)
			{
				Tweener.addTween(_buttons.playPauseButton, { _frame: 15, time: 0.3, transition:"linear" } )
				stopSlideShow()
			} 
			else if(start == true)
			{
				Tweener.addTween(_buttons.playPauseButton, {_frame: 1, time: 0.3, transition:"linear"})
				startSlideShow()
			}
		}
		
		
		//Save and Show
		
		public function saveImage(index:int = -1):void
		{
			if (index == -1) { index = ImageManager.getInstance().index }
			_imageURL = new URLRequest(_data.baseURL + _data.imageFolderURL + _data.getImageURL(index))
			_imageFile = new FileReference()
			_imageFile.addEventListener(ProgressEvent.PROGRESS, onImageSaveProgress)
			_imageFile.addEventListener(Event.COMPLETE, onImageSaveComplete)
			
			_imageFile.download(_imageURL, _data.getImageURL(index))
			
			if (_saveloader == null) 
			{ 
				_saveloader = new Preloader() 
				_saveloader.x = -_saveloader.width / 2
				_saveloader.y = _saveloader.height / 2 + 10
				_layout.addStageObject(_saveloader,{xDistance:0,yDistance:0},LayoutManager.CENTER)
				addChild(_saveloader)
			}
			
			_saveloader.visible = false
		}
		
		private function onImageSaveProgress(e:ProgressEvent):void 
		{
			_saveloader.visible = true
			_saveloader.updatePreloader(Math.round((e.bytesLoaded / e.bytesTotal) * 100))
		}
		
		private function onImageSaveComplete(e:Event):void 
		{
			_saveloader.visible = false
		}
		

		public function showImage(index:int = -1):void
		{
			if (index == -1){index = ImageManager.getInstance().index }
			_imageURL = new URLRequest(_data.imageFolderURL + _data.getImageURL(index))
			navigateToURL(_imageURL,"_blank")
		}
		
		
		public function prevImage():void
		{
			index--
			addImage(index)
		}
		
		public function nextImage():void
		{
			index++
			addImage(index)
		}
		
		public function get index():uint
		{ 
			return  _index
		}
		
		public function set index(value:uint):void 
		{ 
			if (value > -1 && value < _data.pictureCount)
			{
				_index = value
			} else if (value > _data.pictureCount){
				_index = _data.pictureCount - 1
			} 
		}
		
		public function get currentImage():Image
		{
			return _currentImage
		}
		
		public function get isCurrentImageLoaded():Boolean
		{
			return _currentImage.loaded
		}
		
		public function getImage(index:uint):*
		{
			return _imageContainer[index]
		}
		
		public function getCurrentImageURL():String
		{
			return _currentImage.imageURL
		}
		
		public function getCurrentImageSize():String
		{
			return String(_currentImage.size)
		}
		
		public function getCurrentImageType():String
		{
			return _currentImage.type
		}
		
		public function getCurrentImageDimensions():String
		{
			return String(Math.round(_currentImage.width) + " x " +  Math.round(_currentImage.height))
		}
		
		public function getCurrentScale():Number
		{
			return _currentImage.scaleX
		}
		
		public function getCurrentImagePosition():Point
		{
			return new Point(_currentImage.x -(_currentImage.imageWidth / 2), _currentImage.y -(_currentImage.imageHeight / 2))
		}
		
		public function getCurrentImageHeight():Number
		{
			return _currentImage.height
		}
		
		public function getCurrentImageWidth():Number
		{
			return _currentImage.width
		}
		
		public function getCurrentImageRealHeight():Number
		{
			return _currentImage.realHeight
		}
		
		public function getCurrentImageRealWidth():Number
		{
			return _currentImage.realWidth
		}
		
		public function getCurrentImageFrame():int
		{
			return GIFImage(_currentImage).currentFrame
		}
		
		public function getCurrentImageFramesTotal():int
		{
			return GIFImage(_currentImage).totalFrames
		}
		
		public function get slideTimer():Timer
		{
			return _slideTimer
		}
		
		public function set slider(sliderObject:zoomSlider):void
		{
			_slider = sliderObject
		}
	}
}

